x86, vtd: [CVE-2011-1898] Protect against malicious MSIs from untrusted devices.
authorKeir Fraser <keir@xen.org>
Thu, 12 May 2011 15:39:31 +0000 (16:39 +0100)
committerKeir Fraser <keir@xen.org>
Thu, 12 May 2011 15:39:31 +0000 (16:39 +0100)
In the absence of VT-d interrupt remapping support, a device can send
arbitrary APIC messages to host CPUs. One class of attack that results
is to confuse the hypervisor by delivering asynchronous interrupts to
vectors that are expected to handle only synchronous
traps/exceptions.

We block this class of attack by:
(1) setting APIC.TPR=0x10, to block all interrupts below vector
0x20. This blocks delivery to all architectural exception vectors.
(2) checking APIC.ISR[vec] for vectors 0x80 (fast syscall) and 0x82
(hypercall). In these cases we BUG if we detect we are handling a
hardware interrupt -- turning a potentially more severe infiltration
into a straightforward system crash (i.e, DoS).

Thanks to Invisible Things Lab <http://www.invisiblethingslab.com>
for discovery and detailed investigation of this attack.

Signed-off-by: Keir Fraser <keir@xen.org>
xen/arch/x86/apic.c
xen/arch/x86/x86_64/compat/entry.S
xen/arch/x86/x86_64/entry.S
xen/drivers/passthrough/vtd/iommu.c

index 58f809ad42b215183c302d0d6138859b3fd24a55..af335607cd1de0afb90b1d632f9efadd6f2dc1d6 100644 (file)
@@ -560,12 +560,9 @@ void __devinit setup_local_APIC(void)
     init_apic_ldr();
 
     /*
-     * Set Task Priority to 'accept all'. We never change this
-     * later on.
+     * Set Task Priority to reject any interrupts below FIRST_DYNAMIC_VECTOR.
      */
-    value = apic_read(APIC_TASKPRI);
-    value &= ~APIC_TPRI_MASK;
-    apic_write_around(APIC_TASKPRI, value);
+    apic_write_around(APIC_TASKPRI, (FIRST_DYNAMIC_VECTOR & 0xF0) - 0x10);
 
     /*
      * After a crash, we no longer service the interrupts and a pending
@@ -1439,3 +1436,9 @@ int __init APIC_init_uniprocessor (void)
 
     return 0;
 }
+
+void check_for_unexpected_msi(unsigned int vector)
+{
+    unsigned long v = apic_read(APIC_ISR + ((vector & ~0x1f) >> 1));
+    BUG_ON(v & (1 << (vector & 0x1f)));
+}
index dc3b80afe9e9d3658fd9e72468673fd93e71b9ee..3b4f12c1517f68dc49d86939e376f39931848f58 100644 (file)
 #include <asm/page.h>
 #include <asm/desc.h>
 #include <public/xen.h>
+#include <irq_vectors.h>
 
         ALIGN
 ENTRY(compat_hypercall)
         pushq $0
         movl  $TRAP_syscall,4(%rsp)
         SAVE_ALL
+
+        cmpb  $0,untrusted_msi(%rip)
+UNLIKELY_START(ne, msi_check)
+        movl  $HYPERCALL_VECTOR,%edi
+        call  check_for_unexpected_msi
+        RESTORE_ALL
+        SAVE_ALL
+UNLIKELY_END(msi_check)
+
         GET_CURRENT(%rbx)
 
         cmpl  $NR_hypercalls,%eax
index 6cf35f338e9bb048999a44d212bd0ad17bba2fbe..4d49c4331c2dc9843e70a13d429be0d04826b12f 100644 (file)
@@ -297,6 +297,14 @@ ENTRY(int80_direct_trap)
         pushq $0
         SAVE_ALL
 
+        cmpb  $0,untrusted_msi(%rip)
+UNLIKELY_START(ne, msi_check)
+        movl  $0x80,%edi
+        call  check_for_unexpected_msi
+        RESTORE_ALL
+        SAVE_ALL
+UNLIKELY_END(msi_check)
+
         GET_CURRENT(%rbx)
 
         /* Check that the callback is non-null. */
index 8338d3722bc2f2499ce94ef0ee219bf9f1b2da4b..aa3b34aa0aab5ef4040aeaef5e1a33d90b62c1be 100644 (file)
@@ -45,6 +45,9 @@
 #define nr_ioapics              iosapic_get_nr_iosapics()
 #endif
 
+/* Possible unfiltered LAPIC/MSI messages from untrusted sources? */
+bool_t __read_mostly untrusted_msi;
+
 int nr_iommus;
 
 static void setup_dom0_devices(struct domain *d);
@@ -1579,6 +1582,14 @@ static int reassign_device_ownership(
     if (!pdev)
         return -ENODEV;
 
+    /*
+     * Devices assigned to untrusted domains (here assumed to be any domU)
+     * can attempt to send arbitrary LAPIC/MSI messages. We are unprotected
+     * by the root complex unless interrupt remapping is enabled.
+     */
+    if ( (target != dom0) && !iommu_intremap )
+        untrusted_msi = 1;
+
     ret = domain_context_unmap(source, bus, devfn);
     if ( ret )
         return ret;